package com.uinnova.product.eam.web.asset.peer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.binary.core.exception.BinaryException;
import com.binary.core.exception.MessageException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.uinnova.product.eam.base.model.ChangeInfoDto;
import com.uinnova.product.eam.comm.bean.CcAssetCiInfo;
import com.uinnova.product.eam.comm.model.es.ArchDecision;
import com.uinnova.product.eam.comm.model.es.AssetChangeRecord;
import com.uinnova.product.eam.feign.workable.FlowableFeign;
import com.uinnova.product.eam.feign.workable.entity.FLOWACTION;
import com.uinnova.product.eam.feign.workable.entity.PorcessResponse;
import com.uinnova.product.eam.feign.workable.entity.ProcessRequest;
import com.uinnova.product.eam.feign.workable.entity.TaskRequest;
import com.uinnova.product.eam.model.asset.AssetChangeAttrVO;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.constants.FlowableConstant;
import com.uinnova.product.eam.model.vo.ExportCiVO;
import com.uinnova.product.eam.service.IArchDecisionSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.asset.ArchitectureAssetSvc;
import com.uinnova.product.eam.service.asset.AssetChangeRecordSvc;
import com.uinnova.product.eam.service.asset.AssetContent;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.es.IamsESCIPrivateSvc;
import com.uinnova.product.eam.service.es.IamsESCmdbCommDesignSvc;
import com.uinnova.product.eam.web.asset.bean.AssetLisConfSearchParam;
import com.uinnova.product.eam.web.asset.bean.CcCiInfoSaveVo;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.comm.util.CommUtil;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uino.bean.cmdb.base.ESCIAttrDefInfo;
import com.uino.bean.cmdb.base.ESCIClassInfo;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.permission.base.SysUser;
import com.uino.dao.util.ESUtil;
import com.uino.service.permission.microservice.IUserSvc;
import com.uino.util.sys.BeanUtil;
import com.uino.util.sys.LibTypeUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 架构资产
 *
 * @author zdh_c
 */
@Service
@Slf4j
public class ArchitectureAssetPeer implements AssetContent {

    @Value("${http.resource.space}")
    private String httpResourceUrl;
    @Resource
    ICISwitchSvc ciSwitchSvc;

    @Resource
    AssetConfigPeer assetConfigPeer;

    @Resource
    IEamCIClassApiSvc ciClassApiSvc;

    @Resource
    AssetChangeRecordSvc changeRecordSvc;

    @Resource
    IamsESCIPrivateSvc esCiSvc;

    @Resource
    private ESDiagramApiClient diagramApiClient;

    @Resource
    private PlanDesignInstanceService planDesignInstanceService;
    @Resource
    private IArchDecisionSvc archDecisionSvc;

    @Resource
    @Lazy
    private IamsESCmdbCommDesignSvc commSvc;

    @Resource
    private ArchitectureAssetSvc architectureAssetSvc;

    @Autowired
    private BmConfigSvc bmConfigSvc;

    @Autowired
    private FlowableFeign flowableFeign;

    @Autowired
    private IUserSvc userSvc;

    public CcCiInfo saveOrUpdate(CcCiInfoSaveVo saveVo, LibType libType) {
        boolean ifCreator = BinaryUtils.isEmpty(saveVo.getCi().getId());
        //需审批走资产发布流程
        if (LibType.DESIGN.equals(libType)
                && assertPublishNeedApprove(saveVo.getCi().getClassId())) {
            CcCiInfo ccCiInfo = saveorUpdateCi(saveVo, LibType.PRIVATE);
            return startFlow(saveVo.getReleaseDesc(), ccCiInfo);
        }
        CcCiInfo ccCiInfo = saveorUpdateCi(saveVo, libType);
        if (ifCreator) {
            // 添加创建记录
            addAssetChangeRecord(ccCiInfo, CREATE);
        }
        return ccCiInfo;
    }

    public CcCiInfo saveorUpdateCi(CcCiInfo ciInfo, LibType libType) {
        BinaryUtils.checkEmpty(ciInfo.getCi(), "ci");
        if (BinaryUtils.isEmpty(ciInfo.getCi().getOwnerCode())) {
            ciInfo.getCi().setOwnerCode(SysUtil.getCurrentUserInfo().getLoginCode());
        }
        String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        String state;
        String releaseTime = "";
        if (LibType.DESIGN.equals(libType)) {
            state = RELEASE;
            releaseTime = dateTime;
        } else {
            state = DRAFT;
        }
        boolean isCreate = BinaryUtils.isEmpty(ciInfo.getCi().getId());
        if (isCreate) {
            // 新增
            ciInfo.getCi().setCreator(SysUtil.getCurrentUserInfo().getLoginCode());
            ciInfo.getCi().setCreateTime(ESUtil.getNumberDateTime());
            ciInfo.getAttrs().put(CREATION_TIME, dateTime);
            ciInfo.getAttrs().put(MODIFI_TIME, dateTime);
            ciInfo.getAttrs().put(RELEASE_TIME, releaseTime);
            ciInfo.getAttrs().put(RELEASE_STATE, state);
        } else {
            if (APPROVE.equals(ciInfo.getAttrs().get(RELEASE_STATE))) {
                throw new BinaryException("资产审批中，不可编辑");
            }
            if (APPROVE_EDIT.equals(ciInfo.getAttrs().get(RELEASE_STATE))) {
                state = APPROVE_EDIT;
            }
            // 修改
            ciInfo.getAttrs().put(MODIFI_TIME, dateTime);
            ciInfo.getAttrs().put(RELEASE_TIME, dateTime);
            ciInfo.getAttrs().put(RELEASE_STATE, state);
            // 兼容发布
            if (LibType.DESIGN.equals(libType)) {
                List<ESCIInfo> designCiList = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciInfo.getCi().getCiCode()), null, LibType.DESIGN);
                if (CollectionUtils.isEmpty(designCiList)) {
                    ciInfo.getCi().setId(null);
                } else {
                    ciInfo.getCi().setId(designCiList.get(0).getId());
                }
            }
        }
        // 校验是否已存在
        Long id;
        try {
            id = ciSwitchSvc.saveOrUpdateCI(ciInfo, libType);
        } catch (BinaryException e) {
            if (e.getMessage().contains("[设计库]CI已存在,主键冲突:")) {
                String message = e.getMessage();
                throw new BinaryException("【" + message.substring(message.indexOf(":") + 2, message.length() - 1) + "】已存在");
            } else {
                throw e;
            }
        }
        CcCiInfo ciInfoById = ciSwitchSvc.getCiInfoById(id, libType);
        // 添加变更状态
        if (LibType.DESIGN.equals(libType)) {
            // 根据设计库数据更新所有用户私有库数据
            // 同步刷新私有库数据
            updatePrivateCi(ciInfoById);
        }
        return ciInfoById;
    }

    public Long addAssetChangeRecord(CcCiInfo ciInfo, String type) {
        AssetChangeRecord changeRecord = new AssetChangeRecord();
//        changeRecord.setCiLab(ciInfo.getCi().getCiLabel());
//        changeRecord.setCiLab(ciInfo.getCiClass().getClassName());
        changeRecord.setState(2);
        changeRecord.setType(type);
        changeRecord.setCiCode(ciInfo.getCi().getCiCode());
        Map<String, String> attrs = ciInfo.getAttrs();
        String changeReason = attrs.get(CHANGE_RECORD);
        changeRecord.setChangeReason(changeReason);
        if (CHANGE.equals(type)) {
            // 对比变更信息
            List<ESCIInfo> designCiList = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciInfo.getCi().getCiCode()), null, LibType.DESIGN);
            if (CollectionUtils.isEmpty(designCiList)) {
                throw new BinaryException("发布资产信息不存在");
            }
            ESCIInfo esciInfo = designCiList.get(0);
            CcCiInfo ccCiInfo = ciSwitchSvc.getCiInfoById(esciInfo.getId(), LibType.DESIGN);
            Map<String, String> designCiAttr = ccCiInfo.getAttrs();
            List<CcCiAttrDef> attrDefs = ccCiInfo.getAttrDefs();
            Map<String, CcCiAttrDef> attrDefMap = attrDefs.stream().collect(Collectors.toMap(CcCiAttrDef::getProStdName, ccCiAttrDef -> ccCiAttrDef, (k1, k2) -> k1));
            List<AssetChangeAttrVO> changeAttrVOS = new ArrayList<>();
            for (Map.Entry<String, String> entry : attrs.entrySet()) {
                String key = entry.getKey().trim().toUpperCase();
                if (IGNORE_ATTR.contains(key)) {
                    designCiAttr.remove(key);
                    continue;
                }
                AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
                CcCiAttrDef ccCiAttrDef = attrDefMap.get(key);
                if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                    attrVO.setProType(ccCiAttrDef.getProType());
                }
                attrVO.setChangeAttr(key);
                String value = entry.getValue();
                if (!designCiAttr.containsKey(key)) {
                    // 删除属性
                    attrVO.setAfterVal(value);
                    changeAttrVOS.add(attrVO);
                    continue;
                }
                if (designCiAttr.containsKey(key) && !value.equals(designCiAttr.get(key))) {
                    attrVO.setAfterVal(value);
                    attrVO.setBeforeVal(designCiAttr.get(key));
                    changeAttrVOS.add(attrVO);
                    designCiAttr.remove(key);
                    continue;
                }
                if (designCiAttr.containsKey(key) && value.equals(designCiAttr.get(key))) {
                    designCiAttr.remove(key);
                }
            }
            for (Map.Entry<String, String> releaseAttr : designCiAttr.entrySet()) {
                if (BinaryUtils.isEmpty(releaseAttr
                        .getValue())) {
                    continue;
                }
                AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
                changeAttrVOS.add(attrVO);
                attrVO.setChangeAttr(releaseAttr.getKey());
                attrVO.setBeforeVal(releaseAttr.getValue());
                CcCiAttrDef ccCiAttrDef = attrDefMap.get(releaseAttr.getKey());
                if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                    attrVO.setProType(ccCiAttrDef.getProType());
                }
            }
            changeRecord.setChangeContent(JSONObject.toJSONString(changeAttrVOS));
        }
        return changeRecordSvc.saveChangeRecord(changeRecord, SysUtil.getCurrentUserInfo().getLoginCode());
    }

    public CcCiInfo change(CcCiInfo ciInfo) {
        // 添加变更记录
        addAssetChangeRecord(ciInfo, CHANGE);
        return changeCi(ciInfo);
    }

    public CcCiInfo changeCi(CcCiInfo ciInfo) {
        String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        ciInfo.getAttrs().put(MODIFI_TIME, dateTime);
        ciInfo.getAttrs().put(RELEASE_TIME, dateTime);
        ciInfo.getAttrs().put(RELEASE_STATE, RELEASE);
        Long id = ciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.DESIGN);
        CcCiInfo ciInfoById = ciSwitchSvc.getCiInfoById(id, LibType.DESIGN);
        // 设计库发生变更更新私有库数据
        updatePrivateCi(ciInfoById);
        return ciInfoById;
    }

    private void updatePrivateCi(CcCiInfo ciInfoById) {
        CCcCi cCcPrivateCi = new CCcCi();
        cCcPrivateCi.setClassId(ciInfoById.getCi().getClassId());
        cCcPrivateCi.setCiCodes(Lists.newArrayList(ciInfoById.getCi().getCiCode()).toArray(new String[1]));
        // 查询到需更新的私有库数据列表
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        List<CcCiInfo> privateList = ciSwitchSvc.queryCiInfoList(domainId, cCcPrivateCi, null, Boolean.FALSE, Boolean.FALSE, LibType.PRIVATE);
        if (!CollectionUtils.isEmpty(privateList)) {
            for (CcCiInfo ccCiInfo : privateList) {
                ccCiInfo.setAttrs(ciInfoById.getAttrs());
            }
            log.info("=========更新私有库数据: {}", privateList.size());
            // 同步私有库数据
            if (!CollectionUtils.isEmpty(privateList)) {
                LibTypeUtil.execute(() -> {
                    esCiSvc.saveOrUpdateCIBatch(privateList, false);
                }, LibType.PRIVATE);
            }
        } else {
            // 保存发布数据到私有库
            // 保存发布数据到私有库
            CcCi ciPrivate = new CcCi();
            BeanUtils.copyProperties(ciInfoById.getCi(),ciPrivate);
            ciPrivate.setId(null);

            CcCiInfo ciInfoPrivate = new CcCiInfo();
            BeanUtils.copyProperties(ciInfoById,ciInfoPrivate);
            ciInfoPrivate.setCi(ciPrivate);
            ciSwitchSvc.saveOrUpdateCI(ciInfoPrivate, LibType.PRIVATE);
        }
    }

    public List<AssetChangeRecord> getChangeRecordInfo(String ciCode) {
        List<AssetChangeRecord> changeRecord = changeRecordSvc.getAssetChangeRecordByCiCode(ciCode);
        List<ESCIInfo> ciByCodes = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciCode), null, LibType.DESIGN);
        if (CollectionUtils.isEmpty(ciByCodes)) {
            ciByCodes = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciCode), SysUtil.getCurrentUserInfo().getLoginCode(), LibType.PRIVATE);
        }
        String ciLib = "";
        String className = "";
        if (!CollectionUtils.isEmpty(ciByCodes)) {
            ciLib = ciByCodes.get(0).getCiLabel();
            Long classId = ciByCodes.get(0).getClassId();
            ESCIClassInfo classInfo = ciClassApiSvc.queryClassById(classId);
            className = classInfo.getClassName();
        }
        List<String> diagramIds = new ArrayList<>();
        List<Long> planIds = new ArrayList<>();
        List<Long> decisionIds = new ArrayList<>();
        for (AssetChangeRecord assetChangeRecord : changeRecord) {
            assetChangeRecord.setClassName(className);
            assetChangeRecord.setCiLabel(ciLib);
            String diagramChange = assetChangeRecord.getDiagramChange();
            if (!BinaryUtils.isEmpty(diagramChange)) {
                ChangeInfoDto<String> diagramChangeDto = JSONObject.parseObject(diagramChange, new TypeReference<ChangeInfoDto<String>>() {
                });
                diagramIds.addAll(diagramChangeDto.getAdd());
                diagramIds.addAll(diagramChangeDto.getDelete());
            }
            String planChange = assetChangeRecord.getPlanChange();
            if (!BinaryUtils.isEmpty(planChange)) {
                ChangeInfoDto<Long> changeDto = JSONObject.parseObject(planChange, new TypeReference<ChangeInfoDto<Long>>() {
                });
                planIds.addAll(changeDto.getAdd());
                planIds.addAll(changeDto.getDelete());
            }
            String decisionChange = assetChangeRecord.getDecisionChange();
            if (!BinaryUtils.isEmpty(decisionChange)) {
                ChangeInfoDto<Long> changeInfoDto = JSONObject.parseObject(decisionChange, new TypeReference<ChangeInfoDto<Long>>() {
                });
                decisionIds.addAll(changeInfoDto.getAdd());
                decisionIds.addAll(changeInfoDto.getDelete());
            }
        }
        Map<String, String> diagramNameMap = Collections.emptyMap();
        if (!CollectionUtils.isEmpty(diagramIds)) {
            diagramIds = diagramIds.stream().filter(e -> !BinaryUtils.isEmpty(e)).collect(Collectors.toList());
            List<ESDiagram> esDiagrams = diagramApiClient.selectByIds(diagramIds, null, null);
            diagramNameMap = esDiagrams.stream().collect(Collectors.toMap(ESDiagram::getDEnergy, ESDiagram::getName, (k1, k2) -> k1));
        }
        Map<Long, String> planNameMap = Collections.emptyMap();
        if (!CollectionUtils.isEmpty(planIds)) {
            List<PlanDesignInstance> planDesignInstanceList = planDesignInstanceService.getByIds(planIds);
            planNameMap = planDesignInstanceList.stream().collect(Collectors.toMap(PlanDesignInstance::getId, PlanDesignInstance::getName, (k1, k2) -> k1));
        }
        Map<Long, String> decisionNameMap = Collections.emptyMap();
        if (!CollectionUtils.isEmpty(decisionIds)) {
            List<ArchDecision> decisionByIds = archDecisionSvc.getDecisionByIds(decisionIds);
            decisionNameMap = decisionByIds.stream().collect(Collectors.toMap(ArchDecision::getId, ArchDecision::getTitle, (k1, k2) -> k1));
        }
        if (!CollectionUtils.isEmpty(diagramNameMap) || !CollectionUtils.isEmpty(planNameMap) || !CollectionUtils.isEmpty(decisionNameMap)) {
            for (AssetChangeRecord assetChangeRecord : changeRecord) {
                String diagramChange = assetChangeRecord.getDiagramChange();
                if (!BinaryUtils.isEmpty(diagramChange)) {
                    ChangeInfoDto<String> diagramChangeDto = JSONObject.parseObject(diagramChange, new TypeReference<ChangeInfoDto<String>>() {
                    });
                    ChangeInfoDto diagramJson = fillDiagramChangeInfo(diagramNameMap, diagramChangeDto);
                    assetChangeRecord.setDiagramJson(diagramJson);

                }
                String planChange = assetChangeRecord.getPlanChange();
                if (!BinaryUtils.isEmpty(planChange)) {
                    ChangeInfoDto<Long> planChangeJson = JSONObject.parseObject(planChange, new TypeReference<ChangeInfoDto<Long>>() {
                    });
                    ChangeInfoDto planJson = fillChangeInfo(planNameMap, planChangeJson);
                    assetChangeRecord.setPlanJson(planJson);
                }
                String decisionChange = assetChangeRecord.getDecisionChange();
                if (!BinaryUtils.isEmpty(decisionChange)) {
                    ChangeInfoDto<Long> decisionChangeJson = JSONObject.parseObject(decisionChange, new TypeReference<ChangeInfoDto<Long>>() {
                    });
                    ChangeInfoDto decisionJson = fillChangeInfo(decisionNameMap, decisionChangeJson);
                    assetChangeRecord.setDecisionJson(decisionJson);
                }
            }
        }
        return changeRecord;
    }

    private ChangeInfoDto fillChangeInfo(Map<Long, String> planNameMap, ChangeInfoDto<Long> planChangeJson) {
        List<JSONObject> add = new ArrayList<>();
        List<JSONObject> remove = new ArrayList<>();
        for (Long planId : planChangeJson.getAdd()) {
            if (planNameMap.containsKey(planId)) {
                JSONObject e = new JSONObject();
                e.put("id", planId);
                e.put("name", planNameMap.get(planId));
                add.add(e);
            }
        }
        for (Long planId : planChangeJson.getDelete()) {
            if (planNameMap.containsKey(planId)) {
                JSONObject e = new JSONObject();
                e.put("id", planId);
                e.put("name", planNameMap.get(planId));
                remove.add(e);
            }
        }
        ChangeInfoDto planJson = new ChangeInfoDto();
        planJson.setAdd(add);
        planJson.setDelete(remove);
        return planJson;
    }

    private ChangeInfoDto fillDiagramChangeInfo(Map<String, String> diagramNameMap, ChangeInfoDto<String> diagramChangeDto) {
        List<JSONObject> addDiagram = new ArrayList<>();
        List<JSONObject> removeDiagram = new ArrayList<>();
        for (String diagramId : diagramChangeDto.getAdd()) {
            if (diagramNameMap.containsKey(diagramId)) {
                JSONObject e = new JSONObject();
                e.put("id", diagramId);
                e.put("name", diagramNameMap.get(diagramId));
                addDiagram.add(e);
            }
        }
        for (String diagramId : diagramChangeDto.getDelete()) {
            if (diagramNameMap.containsKey(diagramId)) {
                JSONObject e = new JSONObject();
                e.put("id", diagramId);
                e.put("name", diagramNameMap.get(diagramId));
                removeDiagram.add(e);
            }
        }
        ChangeInfoDto diagramJson = new ChangeInfoDto();
        diagramJson.setAdd(addDiagram);
        diagramJson.setDelete(removeDiagram);
        return diagramJson;
    }

    public ResponseEntity<byte[]> exportBySearch(AssetLisConfSearchParam param, LibType libType) {
        param.setPageNum(1);
        param.setPageSize(3000);
        Page<ESCIInfo> infoPage = assetConfigPeer.queryCiInfoPage(libType, param);
        List<ESCIInfo> data = infoPage.getData();
        if (infoPage.getTotalRows() > infoPage.getPageSize()) {
            for (int i = param.getPageNum() + 1; i < 100; i++) {
                param.setPageNum(i);
                Page<ESCIInfo> page = assetConfigPeer.queryCiInfoPage(libType, param);
                if (CollectionUtils.isEmpty(page.getData())) {
                    break;
                }
                data.addAll(page.getData());
            }
        }
        ExportCiVO exportDto = new ExportCiVO();

        if (CollectionUtils.isEmpty(data)) {
            CcCiClassInfo ciClassByCodes = ciClassApiSvc.getCIClassByCodes(param.getClassCode());
            if (BinaryUtils.isEmpty(ciClassByCodes)) {
                throw new BinaryException("分类信息不存在");
            }
            exportDto.setCiClassIds(Sets.newHashSet(ciClassByCodes.getCiClass().getId()));
        } else {
            exportDto.setCiClassIds(Sets.newHashSet(data.get(0).getClassId()));
        }
        // 设置导出字段默认全部导出
//        CcCiClassInfoConfVO attrClassInfo =  listAttrConfSvc.getListShowAttrList(param.getAppSquareConfId(), param.getType());
//        if (!BinaryUtils.isEmpty(attrClassInfo)) {
//            List<Long> attrIdList = new ArrayList<>();
//            List<Long> attrIds = attrClassInfo.getShowListCIAttrInfo().stream().map(ccCiAttrDef -> ccCiAttrDef.getId()).collect(Collectors.toList());
//            attrIdList.addAll(attrIds);
//            List<Long> tagAttrIds = attrClassInfo.getTagListCIAttrInfo().stream().map(ccCiAttrDefConfVO -> ccCiAttrDefConfVO.getId()).collect(Collectors.toList());
//            attrIdList.addAll(tagAttrIds);
//            if (!CollectionUtils.isEmpty(attrIdList)) {
//                HashMap<Long, List<Long>> attrsIdMap = new HashMap<>(1);
//                Set<Long> ciClassIds = exportDto.getCiClassIds();
//                attrsIdMap.put(ciClassIds.toArray(new Long[1])[0], attrIdList);
//                exportDto.setAttrsIdMap(attrsIdMap);
//            }
//        }
        Set<Long> ids = data.stream().map(CcCi::getId).collect(Collectors.toSet());
        exportDto.setHasData(1);
        exportDto.setCiIds(ids);
        exportDto.setHasClsDef(1);
        return ciSwitchSvc.exportCiClassAndAttrs(exportDto, libType);
    }

    public Long cancellation(CcCiInfo ciInfo) {
        // 添加变更记录
        addAssetChangeRecord(ciInfo, CANCELLATION);
        return cancellationCi(ciInfo);
    }

    public Long cancellationCi(CcCiInfo ciInfo) {
        String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        ciInfo.getAttrs().put(MODIFI_TIME, dateTime);
        ciInfo.getAttrs().put(RELEASE_STATE, CANCELLED);
        Long id = ciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.DESIGN);
        CcCiInfo ciInfoById = ciSwitchSvc.getCiInfoById(id, LibType.DESIGN);
        updatePrivateCi(ciInfoById);
        return id;
    }

    public CcAssetCiInfo queryCiAndChangeAttrById(LibType libType, String ciCode, String isContrastReleaseInfo) {
        String loginCode = null;
        if (libType.equals(LibType.PRIVATE)) {
            loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        }
        List<ESCIInfo> ciByCodes = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciCode), loginCode, libType);
        if (CollectionUtils.isEmpty(ciByCodes)) {
            throw new BinaryException("资产信息不存在");
        }
        HashMap<String, Object> changeAttrs = new HashMap<>();
        if (isContrastReleaseInfo.equals("1") && libType.equals(LibType.PRIVATE)) {
            List<ESCIInfo> designCi = ciSwitchSvc.getCiByCodes(Lists.newArrayList(ciCode), null, LibType.DESIGN);
            if (CollectionUtils.isEmpty(designCi)) {
                throw new BinaryException("设计库资产信息不存在");
            }
            Map<String, Object> attrs = ciByCodes.get(0).getAttrs();
            Map<String, Object> designCiAttr = designCi.get(0).getAttrs();
            for (Map.Entry<String, Object> entry : attrs.entrySet()) {
                String key = entry.getKey().trim();
                if (IGNORE_ATTR.contains(key)) {
                    designCiAttr.remove(key);
                    continue;
                }
                Object value = entry.getValue();
                if (!designCiAttr.containsKey(key)) {
                    // 变更新增属性
                    changeAttrs.put(key, "");
                    continue;
                }
                if (designCiAttr.containsKey(key) && !value.equals(designCiAttr.get(key))) {
                    changeAttrs.put(key, designCiAttr.get(key));
                    designCiAttr.remove(key);
                    continue;
                }
                if (designCiAttr.containsKey(key) && value.equals(designCiAttr.get(key))) {
                    designCiAttr.remove(key);
                }
            }
            for (Map.Entry<String, Object> releaseAttr : designCiAttr.entrySet()) {
                if (BinaryUtils.isEmpty(releaseAttr
                        .getValue())) {
                    continue;
                }
                changeAttrs.put(releaseAttr.getKey(), releaseAttr.getValue());
            }
        }
        CcCiInfo ciInfo = commSvc.tranCcCiInfo(ciByCodes.get(0), true);
        CcAssetCiInfo result = new CcAssetCiInfo();
        BeanUtil.copyProperties(ciInfo, result);
        result.setChangeAttrs(changeAttrs);

        String icon = result.getCiClass().getIcon();
        if (icon != null && !icon.startsWith(this.httpResourceUrl)) {
            result.getCiClass().setIcon(httpResourceUrl + icon);
        }
        return result;
    }

    public Object classAndCiProcessingByClassId(List<Long> classIds) {
        return architectureAssetSvc.classAndCiProcessingByClassId(classIds);
    }

    public void checkFillAssetDefAttr(CcCiInfo ciInfo, LibType libType) {
        String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        String state;
        String releaseTime = "";
        if (LibType.DESIGN.equals(libType)) {
            state = RELEASE;
            releaseTime = dateTime;
        } else {
            state = DRAFT;
        }
        ESCIClassInfo classInfo = ciClassApiSvc.queryClassById(ciInfo.getCi().getClassId());
        for (ESCIAttrDefInfo attrDef : classInfo.getAttrDefs()) {
            if (attrDef.isForbidden()) {
                if (BinaryUtils.isEmpty(ciInfo.getCi().getId()) && CREATION_TIME.equals(attrDef.getProName())) {
                    ciInfo.getAttrs().put(CREATION_TIME, dateTime);
                }
                if (MODIFI_TIME.equals(attrDef.getProName())) {
                    ciInfo.getAttrs().put(MODIFI_TIME, dateTime);
                }
                if (RELEASE_TIME.equals(attrDef.getProName())) {
                    ciInfo.getAttrs().put(RELEASE_TIME, releaseTime);
                }
                if (RELEASE_STATE.equals(attrDef.getProName())) {
                    ciInfo.getAttrs().put(RELEASE_STATE, state);
                }
            }
        }
    }

    public boolean assertPublishNeedApprove(Long ciClassId) {
        if (ciClassId == null) {
            return false;
        }
        ESCIClassInfo classInfo = ciClassApiSvc.queryClassById(ciClassId);
        Assert.notNull(classInfo, "分类不存在");
        String configJson = bmConfigSvc.getConfigType("AXEA_CONFIG");
        if (StringUtils.isBlank(configJson)) {
            throw new BinaryException("未查询到国投配置，请联系管理员");
        }
        JSONObject conf = JSON.parseObject(configJson);
        if (conf == null) {
            throw new BinaryException("未查询到国投分类映射配置，请联系管理员");
        }
        String axeaClassMapping = conf.getString("classMapping");
        if (StringUtils.isBlank(axeaClassMapping)) {
            throw new BinaryException("未查询到国投系统分类映射配置，请联系管理员");
        }
        JSONObject axeaClassConf = JSONObject.parseObject(axeaClassMapping);
        String systemClassCode = axeaClassConf.getString("systemClassCode");
        if (StringUtils.isBlank(systemClassCode)) {
            throw new BinaryException("未查询到国投系统分类映射配置，请联系管理员");
        }
        return systemClassCode.equals(classInfo.getClassCode());
    }

    public void checkAssert(CcCiInfo ciInfo) {
        List<ESCIClassInfo> classInfoList = ciClassApiSvc.selectCiClassByIds(Lists.newArrayList(ciInfo.getCi().getClassId()));
        ESCIClassInfo classInfo = classInfoList.get(0);

        List<ESCIAttrDefInfo> attrEsDefs = classInfo.getAttrDefs();
        List<CcCiAttrDef> attrDefs = BeanUtil.converBean(attrEsDefs, CcCiAttrDef.class);
        Map<String, String> attrs = ciInfo.getAttrs();
        //校验业务主键不可为空
        checkMajor(ciInfo, classInfo);
        //校验必填项
        checkMustFillAttr(attrDefs, attrs);
        //校验设计库是否已存在
        checkUniqueness(ciInfo, classInfo.getId(), classInfo.getClassStdCode(), attrDefs, attrs);
    }

    /**
     * 校验主键必填
     *
     * @param ciInfo
     * @param esciClassInfo
     */
    private void checkMajor(CcCiInfo ciInfo, ESCIClassInfo esciClassInfo) {
        List<ESCIAttrDefInfo> attrDefs = esciClassInfo.getAttrDefs();
        List<ESCIAttrDefInfo> attrDefInfoList = attrDefs.stream().filter(e -> 1 == e.getIsMajor()).collect(Collectors.toList());
        Map<String, String> attrs = ciInfo.getAttrs();
        for (ESCIAttrDefInfo esciAttrDefInfo : attrDefInfoList) {
            if (BinaryUtils.isEmpty(attrs.get(esciAttrDefInfo.getProName()))) {
                throw new BinaryException("【" + esciAttrDefInfo.getProName() + "】属性为主键不可为空");
            }
        }
    }

    /**
     * 校验必填
     *
     * @param attrDefs
     * @param attrs
     */
    private void checkMustFillAttr(List<CcCiAttrDef> attrDefs, Map<String, String> attrs) {
        Map<String, Integer> checkResult = commSvc.validAttrs(attrDefs, attrs, true);
        if (!BinaryUtils.isEmpty(checkResult)) {
            for (String result : checkResult.keySet()) {
                throw new MessageException(result);
            }
        }
    }

    /**
     * 校验唯一性
     *
     * @param ciInfo
     * @param classId
     * @param classStdCode
     * @param attrDefs
     * @param attrs
     */
    private void checkUniqueness(CcCiInfo ciInfo, Long classId, String classStdCode, List<CcCiAttrDef> attrDefs, Map<String, String> attrs) {
        CcCi ci = ciInfo.getCi();
        String ciCode = ci.getCiCode();
        List<String> ciPKAttrDefNames = CommUtil.getCiPKAttrDefNames(attrDefs);
        Integer hashCode = CommUtil.getCiMajorHashCode(attrs, ciPKAttrDefNames, classStdCode);
        List<String> ciPrimaryKeys = CommUtil.getCiPrimaryKeys(classStdCode, attrs, ciPKAttrDefNames);
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.termQuery("hashCode", hashCode));
        boolQuery.must(QueryBuilders.termQuery("classId", classId));
        boolQuery.must(QueryBuilders.termQuery("domainId", 1L));
        List<CcCiInfo> liveCis = esCiSvc.getCIInfoPageByQuery(1, 3000, boolQuery, false).getData();
        for (CcCiInfo cInfo : liveCis) {
            if (compareCiPrimaryKeys(ciPrimaryKeys, com.binary.json.JSON.toList(cInfo.getCi().getCiPrimaryKey(), String.class))) {
                // 同分类下ciCode或id相同，更新，否则删除
                // 有id时优先按id更新，兼容仅有ciCode时更新的情况
                boolean idEqual = ci.getId() != null && ci.getId().longValue() == cInfo.getCi().getId().longValue();
                boolean codeEqual = ciCode != null && ciCode.equals(cInfo.getCi().getCiCode());
                boolean classIdEqual = ci.getClassId().longValue() == cInfo.getCi().getClassId().longValue();
                boolean isUpdate = (codeEqual || idEqual) && classIdEqual;
                if (isUpdate) {
                    if (ci.getId() != null && ciCode != null) {
                        if (!codeEqual || !idEqual) {
                            throw new BinaryException("[设计库]CI已存在,主键冲突-:" + ciPrimaryKeys);
                        }
                    }
                    ci.setId(cInfo.getCi().getId());
                } else {
                    throw new BinaryException("[设计库]CI已存在,主键冲突:" + ciPrimaryKeys);
                }
            }
        }
    }

    private boolean compareCiPrimaryKeys(List<String> ciPks1, List<String> ciPks2) {
        try {
            return CommUtil.compareToCiPks(ciPks1, ciPks2);
        } catch (Exception e) {
            log.error("do compareToCiPks 【" + com.binary.json.JSON.toString(ciPks1) + "】 and 【 " + com.binary.json.JSON.toString(ciPks2) + "】 err!");
        }
        return false;
    }



    private CcCiInfo startFlow(String releaseDesc, CcCiInfo ccCiInfo) {
        if (!DRAFT.equals(ccCiInfo.getAttrs().get(RELEASE_STATE))) {
            throw new BinaryException("资产非草稿态，不允许发布");
        }
        checkAssert(ccCiInfo);

        List<String> nextUserIds = queryAssertApprovalUsers();
        if (CollectionUtils.isEmpty(nextUserIds)) {
            throw new BinaryException("未查询到审批角色信息，请联系管理员处理");
        }

        ProcessRequest processRequest = new ProcessRequest();
        processRequest.setProcessDefinitionKey(FlowableConstant.GT_ASSERT_APPROVE);
        processRequest.setProcessInstanceName(getFlowName(ccCiInfo));
        processRequest.setBusinessKey(ccCiInfo.getCi().getId().toString());
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        processRequest.setUserId(curUserLoginCode);
        processRequest.setOwner(curUserLoginCode);
        if (StringUtils.isNotBlank(releaseDesc)) {
            Map<String, Object> routerVariables = new HashMap<>();
            routerVariables.put("releaseDesc", releaseDesc);
            processRequest.setRouterVariables(routerVariables);
        }

        PorcessResponse response = flowableFeign.startProcessBindAssignee(processRequest);
        if (StringUtils.isBlank(response.getTaskId())) {
            log.error("资产评审流程发起失败:params[{}],res:{}", JSON.toJSONString(processRequest), JSON.toJSONString(response));
            throw new BinaryException("资产评审流程发起失败");
        }

        TaskRequest taskRequest = new TaskRequest();
        taskRequest.setAction(FLOWACTION.ACCETP);
        taskRequest.setTaskId(response.getTaskId());
        taskRequest.setNextUserIds(String.join(",", nextUserIds));
        flowableFeign.completeTask(taskRequest);

        String dateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        //资产状态置成审批中
        ccCiInfo.getAttrs().put(RELEASE_STATE, APPROVE);
        ccCiInfo.getAttrs().put(MODIFI_TIME, dateTime);
        Long id = ciSwitchSvc.saveOrUpdateCI(ccCiInfo, LibType.PRIVATE);
        return ciSwitchSvc.getCiInfoById(id, LibType.PRIVATE);
    }

    private String getFlowName(CcCiInfo ccCiInfo) {
        String ciClassName = ccCiInfo.getCiClass().getClassName();
        List<String> ciLabels = JSONArray.parseArray(ccCiInfo.getCi().getCiLabel(), String.class);
        return ciClassName.concat("-").concat(String.join(",", ciLabels)).concat("资产评审");
    }

    private List<String> queryAssertApprovalUsers() {
        String configJson = bmConfigSvc.getConfigType("AXEA_CONFIG");
        if (StringUtils.isBlank(configJson)) {
            throw new BinaryException("未查询到国投配置，请联系管理员");
        }
        JSONObject conf = JSON.parseObject(configJson, JSONObject.class);
        if (StringUtils.isBlank(conf.getString("assertApproveConf"))) {
            throw new BinaryException("未查询到国投资产审批配置，请联系管理员");
        }
        JSONObject assertApproveConf = JSON.parseObject(conf.getString("assertApproveConf"));
        if (StringUtils.isBlank(assertApproveConf.getString("roleNames"))) {
            throw new BinaryException("未查询到审批角色信息，请联系管理员处理");
        }
        List<String> roleNames = JSON.parseArray(assertApproveConf.getString("roleNames"), String.class)
                .stream().filter(StringUtils::isNotBlank).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(roleNames)) {
            throw new BinaryException("未查询到审批角色信息，请联系管理员处理");
        }
        List<SysUser> users = new ArrayList<>();
        //查角色下的用户
        for (String roleName : roleNames) {
            List<SysUser> userList;
            try {
                userList = userSvc.getUserByRoleNameAndCount(roleName, 200);
            } catch (Exception e) {
                if ("没有对应的角色".equals(e.getMessage())) {
                    throw new BinaryException("未查询到审批角色信息，请联系管理员处理");
                }
                throw new BinaryException(e);
            }
            if (CollectionUtils.isEmpty(userList)) {
                throw new BinaryException(String.format("%s下未配置用户信息，请联系管理员处理", roleName));
            }
            users.addAll(userList);
        }
        if (users.size() > 200) {
            users = users.subList(0, 200);
        }
        return users.stream().map(SysUser::getLoginCode).distinct().collect(Collectors.toList());
    }
}
